package org.example.system.service.impl.oss;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import io.minio.errors.MinioException;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.example.system.config.properties.AliYunOssProperties;
import org.example.system.model.dto.FileInfo;
import org.example.system.model.dto.TreeFileList;
import org.example.system.service.OssService;
import org.example.system.service.SysUserService;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

@Service(value = "aliYunOssService")
public class AliYunOssServiceImpl implements OssService {

    @Resource
    private AliYunOssProperties aliYunOssProperties;

    @Resource
    private SysUserService sysUserService;

    private OSS ossClient;

    @PostConstruct
    public void init() {
        // 创建OSSClient实例
        ossClient =
                new OSSClientBuilder().build(
                        aliYunOssProperties.getEndpoint(),
                        aliYunOssProperties.getAccessKeyId(),
                        aliYunOssProperties.getAccessKeySecret());
    }


    /**
     * 文件上传到 Minio 服务器
     *
     * @param uploadFile 文件对象
     * @return 文件上传信息
     */
    private FileInfo uploadToOss(MultipartFile uploadFile) {
        // 生成文件名(日期文件夹)
        String originalFilename = uploadFile.getOriginalFilename();
        if (StrUtil.isBlank(originalFilename)) {
            throw new RuntimeException("文件名不能为空");
        }

        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        //为了避免上传的文件重名,重新生成一个新的文件名
        originalFilename = UUID.randomUUID().toString().replace("-","") + fileSuffix;

        // 文件名相同会进行覆盖
        String fileName = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + "/" + originalFilename;
        //  try-with-resource 语法糖自动释放流
        try (InputStream inputStream = uploadFile.getInputStream()) {
            // 上传文件到阿里云OSS对象存储里面
            ossClient.putObject(
                    aliYunOssProperties.getBucketName(),
                    fileName,
                    inputStream);
            // 返回文件路径
            //上传完成后   :文件的访问路径为 https://桶名.地域节点/yyyyMMdd/UUID.jpeg
            String fileUrl = aliYunOssProperties.getUrlPrefix() + "/" + fileName;;
            FileInfo fileInfo = new FileInfo();
            fileInfo.setName(fileName);
            fileInfo.setUrl(fileUrl);
            return fileInfo;
        } catch (Exception e) {
            throw new RuntimeException("文件上传失败");
        }
    }

    /**
     * 上传文件
     *
     * @param file 表单文件对象
     * @return 文件信息
     */
    @Override
    public FileInfo uploadFile(MultipartFile file) {
        return uploadToOss(file);
    }

    /**
     * 上传头像
     *
     * @param file 表单文件对象
     * @return 文件信息
     */
    @Override
    public FileInfo uploadAvatar(MultipartFile file) {
        FileInfo fileInfo = this.uploadToOss(file);
        // 更新数据库中当前用户的头像存储地址
        boolean b = sysUserService.updateAvatarUrl(fileInfo.getUrl());
        if (b) {
            return fileInfo;
        } else {
            throw new RuntimeException("头像上传失败");
        }
    }

    /**
     * 下载文件
     *
     * @param objectName 文件名
     */
    @Override
    public ResponseEntity<InputStreamResource> downLoadFile(String objectName) {
        try {
            OSSObject ossObject = ossClient.getObject(aliYunOssProperties.getBucketName(), objectName);
            InputStream inputStream = ossObject.getObjectContent();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputStream.readAllBytes());
            // 设置响应头触发浏览器下载
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            // objectName = yyyyMMdd/UUID
            headers.setContentDispositionFormData("attachment", objectName);
            return ResponseEntity.ok().headers(headers).body(new InputStreamResource(byteArrayInputStream));
        } catch (Exception e) {
            throw new RuntimeException("文件下载失败");
        }
    }



    /**
     * 删除单个文件
     *
     * @param fileName 文件名
     */
    @Override
    public void deleteFile(String fileName) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        ossClient.deleteObject(aliYunOssProperties.getBucketName(), fileName);
    }

    /**
     * 批量删除文件
     *
     * @param fileNameList 文件名集合
     */
    @Override
    public void deleteFileBatch(List<String> fileNameList) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        ossClient.deleteObjects(new DeleteObjectsRequest(aliYunOssProperties.getBucketName()).withKeys(fileNameList));
    }


    /**
     * 查看文件列表
     *
     * @return 文件集合
     */
    @Override
    public List<TreeFileList> getFileList() throws NoSuchAlgorithmException, IOException, InvalidKeyException, MinioException {
        // 设置最大个数。
        final int maxKeys = 200;
        // 列举文件。
        ObjectListing objectListing = ossClient.listObjects(new ListObjectsRequest(aliYunOssProperties.getBucketName()).withMaxKeys(maxKeys));
        List<OSSObjectSummary> fileList = objectListing.getObjectSummaries();
        return List.of();
    }

    /**
     * 批量文件上传
     *
     * @param files 文件列表
     * @return 上传后的文件信息集合
     */
    @Override
    public List<FileInfo> uploadFiles(List<MultipartFile> files) {
        return List.of();
    }

    /**
     * 文件批量下载
     *
     * @param fileNameList 文件名列表
     * @return zip 文件
     */
    @Override
    public ResponseEntity<byte[]> downLoadFileZip(List<String> fileNameList) {
        return null;
    }
}
